﻿using System;
using System.Collections.Generic;
using System.Text;
using Ex03.GarageLogic;

namespace Ex03.GarageManagementSystem.ConsoleUI
{
    public class GarageManagementSystemUI
    {
        private Garage m_Garage;

        private enum eMainMenuChoice
        {
            AddVehicle,
            ShowLicensingNumbers,
            ChangeVehicleStatus,
            InflateWheels,
            Refuel,
            ChargeBattery,
            ShowVehicleInfo,
            Exit
        }

        private enum eUserChoiceRegardingFilter
        {
            Yes,
            No
        }

        public GarageManagementSystemUI()
        {
            m_Garage = new Garage();
        }

        public void StartUI()
        {
            showGreeting();
            startMainMenu();
            waitForUserToExit();
        }

        private void showGreeting()
        {
            string greetingMessage = string.Format("Dear user,{0}Welcome to the garage management system!", Environment.NewLine);
            Console.WriteLine(greetingMessage);
        }

        private void startMainMenu()
        {
            eMainMenuChoice mainMenuChoice = getMainMenuChoiceFromUser();

            while (mainMenuChoice != eMainMenuChoice.Exit)
            {
                switch (mainMenuChoice)
                {
                    case eMainMenuChoice.AddVehicle:
                        addNewVehicle();
                        break;
                    case eMainMenuChoice.ShowLicensingNumbers:
                        showLicensingNumbers();
                        break;
                    case eMainMenuChoice.ChangeVehicleStatus:
                        changeVehicleStatus();
                        break;
                    case eMainMenuChoice.InflateWheels:
                        inflateWheels();
                        break;
                    case eMainMenuChoice.Refuel:
                        refuel();
                        break;
                    case eMainMenuChoice.ChargeBattery:
                        chargeBattery();
                        break;
                    case eMainMenuChoice.ShowVehicleInfo:
                        showVehicleInfo();
                        break;
                }

                Console.WriteLine("Press any key to continue.");
                Console.ReadLine();
                Console.Clear();
                mainMenuChoice = getMainMenuChoiceFromUser();
            }
        }

        private eMainMenuChoice getMainMenuChoiceFromUser()
        {
            eMainMenuChoice mainMenuChoice;
            
            Console.WriteLine("What would you like to do?");
            Console.WriteLine("1. Add a new vehicle to the garage.");
            Console.WriteLine("2. Show a list of licensing numbers of vehicles (possible to filter by vehicles status.)");
            Console.WriteLine("3. Change a vehicle's status.");
            Console.WriteLine("4. Inflate a vehicle's wheels.");
            Console.WriteLine("5. Refuel a fuel vehicle.");
            Console.WriteLine("6. Charge an electric vehicle's battery.");
            Console.WriteLine("7. Show a vehicle's full details.");
            Console.WriteLine("8. Exit the application.");

            string userInputString = Console.ReadLine();
            switch (userInputString)
            {
                case "1":
                    mainMenuChoice = eMainMenuChoice.AddVehicle;
                    break;
                case "2":
                    mainMenuChoice = eMainMenuChoice.ShowLicensingNumbers;
                    break;
                case "3":
                    mainMenuChoice = eMainMenuChoice.ChangeVehicleStatus;
                    break;
                case "4":
                    mainMenuChoice = eMainMenuChoice.InflateWheels;
                    break;
                case "5":
                    mainMenuChoice = eMainMenuChoice.Refuel;
                    break;
                case "6":
                    mainMenuChoice = eMainMenuChoice.ChargeBattery;
                    break;
                case "7":
                    mainMenuChoice = eMainMenuChoice.ShowVehicleInfo;
                    break;
                case "8":
                    mainMenuChoice = eMainMenuChoice.Exit;
                    break;
                default:
                    Console.WriteLine("You typed an incorrect input. Please choose one of the possible options.");
                    mainMenuChoice = getMainMenuChoiceFromUser();
                    break;
            }

            return mainMenuChoice;
        }

        private void addNewVehicle()
        {
            VehicleOwner vehicleOwner = getVehicleOwnerFromUser();
            VehicleFactory.eVehicleType vehicleType = getVehicleTypeFromUser();
            Dictionary<string, string> properties = getPropertiesForVehicleFromUser(vehicleType);

            try
            {
                m_Garage.AddVehicle(VehicleFactory.CreateVehicle(vehicleType, properties), vehicleOwner);
                Console.WriteLine("Vehicle added.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to add the vehicle for the following reason:{0}{1}", Environment.NewLine, ex.Message);
            }
        }

        private Dictionary<string, string> getPropertiesForVehicleFromUser(VehicleFactory.eVehicleType i_VehicleType)
        {
            Dictionary<string, string> returnProperties = new Dictionary<string, string>();
            List<string> properties = VehicleFactory.GetPropertiesOfVehicleType(i_VehicleType);

            Console.WriteLine("Please enter the following information regarding the vehicle you asked to add.");

            foreach (string property in properties)
            {
                string userInputString;

                Console.WriteLine(property);
                userInputString = Console.ReadLine();
                returnProperties.Add(property, userInputString);
            }

            return returnProperties;
        }

        private VehicleFactory.eVehicleType getVehicleTypeFromUser()
        {
            int index = 1;
            string userInputString;
            int userInput;
            bool parsingSuccess;
            VehicleFactory.eVehicleType vehicleType;
            string[] vehicleTypeNames = Enum.GetNames(typeof(VehicleFactory.eVehicleType));

            Console.WriteLine("Please choose the type of vehicle you would like to add to the garage.");

            foreach (string vehicleTypeName in vehicleTypeNames)
            {
                Console.WriteLine("{0}. {1}.", index.ToString(), vehicleTypeName);
                index++;
            }

            userInputString = Console.ReadLine();
            parsingSuccess = int.TryParse(userInputString, out userInput);

            if (!parsingSuccess || userInput < 1 || userInput > vehicleTypeNames.Length)
            {
                Console.WriteLine("You typed an incorrect input. Please choose one of the possible options.");
                vehicleType = getVehicleTypeFromUser();
            }
            else
            {
                vehicleType =
                    (VehicleFactory.eVehicleType)Enum.Parse(typeof(VehicleFactory.eVehicleType), vehicleTypeNames[userInput - 1]);
            }

            return vehicleType;
        }

        private VehicleOwner getVehicleOwnerFromUser()
        {
            string ownerName;
            string ownerPhone;

            Console.WriteLine("Please enter the name of the owner of the vehicle.");
            ownerName = Console.ReadLine();
            Console.WriteLine("Please enter the phone number of the owner of the vehicle.");
            ownerPhone = Console.ReadLine();

            return new VehicleOwner(ownerName, ownerPhone);
        }

        private void showLicensingNumbers()
        {
            List<Vehicle> vehiclesToShow = null;
            eUserChoiceRegardingFilter userChoiceRegardingFilter = getUserChoiceRegardingFilter();
            
            switch (userChoiceRegardingFilter)
            {
                case eUserChoiceRegardingFilter.Yes:
                    eVehicleStatus vehicleStatus = getVehicleStatusFromUser();
                    vehiclesToShow = m_Garage.GetVehiclesByVehicleStatus(vehicleStatus);
                    break;
                case eUserChoiceRegardingFilter.No:
                    vehiclesToShow = m_Garage.GetAllVehicles();
                    break;
            }

            Console.WriteLine("The licensing numbers of the vehicles you asked to show are:");

            foreach (Vehicle vehicle in vehiclesToShow)
            {
                Console.WriteLine(vehicle.LicensingNumber);
            }
        }

        private eUserChoiceRegardingFilter getUserChoiceRegardingFilter()
        {
            string userInputFilter;
            eUserChoiceRegardingFilter userChoiceRegardingFilter;

            Console.WriteLine("Would you like to filter by vehicles status?");
            Console.WriteLine("Type 1 for yes, and 2 for no.");

            userInputFilter = Console.ReadLine();

            if (userInputFilter == "1")
            {
                userChoiceRegardingFilter = eUserChoiceRegardingFilter.Yes;
            }
            else if (userInputFilter == "2")
            {
                userChoiceRegardingFilter = eUserChoiceRegardingFilter.No;
            }
            else
            {
                Console.WriteLine("You typed an incorrect input. Please choose one of the possible options.");
                userChoiceRegardingFilter = getUserChoiceRegardingFilter();
            }

            return userChoiceRegardingFilter;
        }

        private eVehicleStatus getVehicleStatusFromUser()
        {
            eVehicleStatus vehicleStatus;

            Console.WriteLine("Please choose the status.");
            Console.WriteLine("1. Repairing.");
            Console.WriteLine("2. Repaired.");
            Console.WriteLine("3. Paid.");

            string userInputString = Console.ReadLine();
            switch (userInputString)
            {
                case "1":
                    vehicleStatus = eVehicleStatus.Repairing;
                    break;
                case "2":
                    vehicleStatus = eVehicleStatus.Repaired;
                    break;
                case "3":
                    vehicleStatus = eVehicleStatus.Paid;
                    break;
                default:
                    Console.WriteLine("You typed an incorrect input. Please choose one of the possible options.");
                    vehicleStatus = getVehicleStatusFromUser();
                    break;
            }

            return vehicleStatus;
        }

        private void changeVehicleStatus()
        {
            string licensingNumber = getLicensingNumberFromUser();
            eVehicleStatus vehicleStatus = getVehicleStatusFromUser();

            try
            {
                m_Garage.SetVehicleStatus(licensingNumber, vehicleStatus);
                Console.WriteLine("Status changed.");
            }
            catch (VehicleNotFoundException ex)
            {
                Console.WriteLine("The vehicle with the licensing number {0} does not exist in the garage.", ex.LicensingNumber);
            }
        }

        private string getLicensingNumberFromUser()
        {
            string licensingNumber;

            Console.WriteLine("Please enter the licensing number of the vehicle.");
            licensingNumber = Console.ReadLine();

            return licensingNumber;
        }

        private void inflateWheels()
        {
            string licensingNumber = getLicensingNumberFromUser();

            try
            {
                m_Garage.InflateWheelsToMaximum(licensingNumber);
                Console.WriteLine("Wheels inflated to maximum.");
            }
            catch (VehicleNotFoundException ex)
            {
                Console.WriteLine("The vehicle with the licensing number {0} does not exist in the garage.", ex.LicensingNumber);
            }
        }

        private void refuel()
        {
            string licensingNumber = getLicensingNumberFromUser();
            float amountOfFuel = getAmountOfFuelToAddFromUser();
            eFuelType fuelType = getFuelTypeFromUser();

            try
            {
                m_Garage.Refuel(licensingNumber, amountOfFuel, fuelType);
                Console.WriteLine("Vehicle refueled.");
            }
            catch (VehicleNotFoundException ex)
            {
                Console.WriteLine("The vehicle with the licensing number {0} does not exist in the garage.", ex.LicensingNumber);
            }
            catch (ValueOutOfRangeException ex)
            {
                Console.WriteLine("{0} is too much fuel for this tank.", ex.ValueAfterChange);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private eFuelType getFuelTypeFromUser()
        {
            eFuelType fuelType;
        
            Console.WriteLine("Please choose the fuel type.");
            Console.WriteLine("1. Octan98.");
            Console.WriteLine("2. Octan96.");
            Console.WriteLine("3. Octan95.");
            Console.WriteLine("4. Soler.");

            string userInputString = Console.ReadLine();
            switch (userInputString)
            {
                case "1":
                    fuelType = eFuelType.Octan98;
                    break;
                case "2":
                    fuelType = eFuelType.Octan96;
                    break;
                case "3":
                    fuelType = eFuelType.Octan95;
                    break;
                case "4":
                    fuelType = eFuelType.Soler;
                    break;
                default:
                    Console.WriteLine("You typed an incorrect input. Please choose one of the possible options.");
                    fuelType = getFuelTypeFromUser();
                    break;
            }

            return fuelType;
        }

        private float getAmountOfFuelToAddFromUser()
        {
            float amountOfFuel;
            bool parsingSuccess;
            string userInputString;

            Console.WriteLine("Please enter the amount of litres of fuel to add to the vehicle.");
            userInputString = Console.ReadLine();
            parsingSuccess = float.TryParse(userInputString, out amountOfFuel);

            if (!parsingSuccess)
            {
                Console.WriteLine("Your input is incorrect.");
                amountOfFuel = getAmountOfFuelToAddFromUser();
            }

            return amountOfFuel;
        }

        private void chargeBattery()
        {
            string licensingNumber = getLicensingNumberFromUser();
            float numberOfHoursToCharge = getNumbersOfHoursToChargeFromUser();

            try
            {
                m_Garage.ChargeBattery(licensingNumber, numberOfHoursToCharge);
                Console.WriteLine("Vehicle's battery charged.");
            }
            catch (VehicleNotFoundException ex)
            {
                Console.WriteLine("The vehicle with the licensing number {0} does not exist in the garage.", ex.LicensingNumber);
            }
            catch (ValueOutOfRangeException ex)
            {
                Console.WriteLine("{0} is more than the maximal number of battery hours.", ex.ValueAfterChange);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private float getNumbersOfHoursToChargeFromUser()
        {
            float numberOfMinutesToCharge;
            bool parsingSuccess;
            string userInputString;

            Console.WriteLine("Please enter the number of minutes to charge the battery of the vehicle.");
            userInputString = Console.ReadLine();
            parsingSuccess = float.TryParse(userInputString, out numberOfMinutesToCharge);

            if (!parsingSuccess)
            {
                Console.WriteLine("Your input is incorrect.");
                numberOfMinutesToCharge = getNumbersOfHoursToChargeFromUser();
            }

            return numberOfMinutesToCharge / 60;
        }

        private void showVehicleInfo()
        {
            string licensingNumber = getLicensingNumberFromUser();

            try
            {
                VehicleInGarage vehicleInGarage = m_Garage.GetVehicleInGarageByLicensingNumber(licensingNumber);
                Dictionary<string, string> properties = vehicleInGarage.GetAllProperties();

                Console.WriteLine("The properties of the vehicle are:");

                foreach (KeyValuePair<string, string> property in properties)
                {
                    Console.WriteLine("{0}: {1}", property.Key, property.Value);
                }
            }
            catch (VehicleNotFoundException ex)
            {
                Console.WriteLine("The vehicle with the licensing number {0} does not exist in the garage.", ex.LicensingNumber);
            }
        }

        private void waitForUserToExit()
        {
            string pressAnyKeyMessage = "Press any key to continue . . .";
            Console.WriteLine(pressAnyKeyMessage);
            Console.ReadLine();
        }
    }
}
